﻿public class CodeWriter
{
    private bool _newLine;
    public int Indent { get; set; }
    private readonly TextWriter _writer;
    public CodeWriter(TextWriter writer)
    {
        _writer = writer;
    }
    public void Code(string code)
    {
        CheckNewLine();
        _writer.Write(code);
    }
    public void Line()
    {
        CheckNewLine();
        _writer.WriteLine();
        _newLine = true;
    }
    public void Line(string code)
    {
        CheckNewLine();
        _writer.WriteLine(code);
        _newLine = true;
    }
    
    private void CheckNewLine()
    {
        if (_newLine)
        {
            _newLine = false;
            var count = Indent * 4;
            for (var i = 0; i < count; i++)
            {
                _writer.Write(' ');
            }
        }
    }
}
/*
string[] ExcludeTypes = {
    "<Module>",
    "Microsoft.CodeAnalysis.EmbeddedAttribute",
    "System.Runtime.CompilerServices.NullableAttribute",
    "System.Runtime.CompilerServices.NullableContextAttribute",
};
var assembly = AssemblyDefinition.ReadAssembly("IL2LuaTest.dll");
var module = assembly.MainModule;
module.ReadSymbols();
var roleService = Services.GetService<IRoleService>();
var types = new List<LuaType>();

foreach (var type in module.Types)
{
    if (ExcludeTypes.Any(x => x == type.FullName))
        continue;
    var typeRole = roleService.GetRole(type);
    var fields = new List<LuaVariable>();
    foreach (var field in type.Fields)
    {
        var fieldRole = roleService.GetRole(field);
        fields.Add(new LuaVariable(fieldRole, VariableFlag.Field));
        Console.WriteLine($"{fieldRole.FullName}");
    }

    var methods = new List<LuaMethod>();
    foreach (var method in type.Methods)
    {
        var methodContext = new MethodContext(method);
        foreach (var param in method.Parameters)
        {
            if (param.IsOut || param.IsReturnValue)
                methodContext.PushReturn(methodContext.GetRole(param));
            methodContext.PushParam(new LuaVariable(methodContext.GetRole(param), VariableFlag.Param));
        }
        foreach (var local in method.Body.Variables)
        {
            methodContext.PushLoacl(new LuaVariable(methodContext.GetRole(local), VariableFlag.Local));
        }
        var luaMethod = new LuaMethod(roleService.GetRole(method), methodContext.Parameters, methodContext.Locals, methodContext.Returns);
        methods.Add(luaMethod);
        PrintMethod(method, luaMethod, methodContext);
    }
    types.Add(new LuaType(typeRole, methods.ToArray(), fields.ToArray()));
}

void PrintCallInstruction(Stack<Instruction> stack, Instruction instruction, MethodContext methodContext)
{
    var method = instruction.Operand as MethodReference;
    if (method != null)
    {
        var methodRole = roleService.GetRole(method);
        var paramCount = method.HasThis ? method.Parameters.Count + 1 : method.Parameters.Count;

        Console.Write($"    {methodRole.FullName}(");
        for (var i = 0; i < paramCount; i++)
        {
            if (i != 0)
                Console.Write(", ");
            var operand = stack.Pop().Operand;
            if (operand is ParameterDefinition param && param.Index == -1)
                Console.Write("self");
            else
                Console.Write(operand);

        }
        Console.WriteLine(")");
    }

}

void PrintNewArrayInstruction(Stack<Instruction> stack, Instruction instruction, MethodContext methodContext)
{
    var size = (int)stack.Pop().Operand;
    Console.Write("    { ");
    for (var i = 1; i < size + 1; i++)
    {
        if (i > 1)
            Console.Write($", nil");
        else
            Console.Write("nil");
    }
    Console.WriteLine(" }");

}


void PrintNewObjInstruction(Stack<Instruction> stack, Instruction instruction, MethodContext methodContext)
{
    var method = instruction.Operand as MethodReference;
    if (method != null)
    {
        var methodRole = roleService.GetRole(method);
        var paramCount = method.Parameters.Count;

        Console.Write($"    new({methodRole.FullName}");
        for (var i = 0; i < paramCount; i++)
        {
            Console.Write(", ");
            var operand = stack.Pop().Operand;
            if (operand is ParameterReference param)
            {
                if (param.Index == -1)
                    Console.Write("self");
                else
                    Console.Write(methodContext.GetRole(param).FullName);
            }
            else if (operand is VariableReference variable)
            {
                Console.Write(methodContext.GetRole(variable).FullName);
            }
            else
                Console.Write(operand);

        }
        Console.WriteLine(")");
    }
}
void PrintSetFieldInstruction(Stack<Instruction> stack, Instruction instruction, MethodContext methodContext)
{
    var field = instruction.Operand as FieldReference;
    var fieldRole = roleService.GetRole(field);
    var value = stack.Pop().Operand;
    var instance = stack.Pop().Operand;
    if (instance is ParameterDefinition param && param.Index == -1)
        instance = methodContext.Parameters[0].Name.Name;
    Console.WriteLine($"    {instance}.{fieldRole.Name} = {value}");
}

void PrintSetLocalInstruction(Stack<Instruction> stack, Instruction instruction, MethodContext methodContext)
{
    var var = instruction.Operand as VariableDefinition;
    var localRole = methodContext.Locals[var.Index];
    Console.WriteLine($"    {localRole.Name.FullName} = {stack.Pop().Operand}");
}
void PrintMethod(MethodDefinition method, LuaMethod luaMethod, MethodContext methodContext)
{
    Console.WriteLine($"{luaMethod.Name.FullName}({string.Join(", ", luaMethod.Parameters.Select(x => x.Name.Name))})");
    method.Body.SimplifyMacros();
    var color = Console.ForegroundColor;
    Console.ForegroundColor = ConsoleColor.White;
    foreach (var loc in methodContext.Locals)
        Console.WriteLine($"    local {loc.Name.FullName}");
    var stack = new Stack<Instruction>();
    if (method.HasBody)
    {
        foreach (var inst in method.Body.Instructions)
        {
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.WriteLine("    " + inst);
            Console.ForegroundColor = ConsoleColor.White;
            switch (inst.OpCode.Code)
            {
                case Code.Nop:
                    break;
                case Code.Ldloc:
                case Code.Ldarg:
                case Code.Ldfld:
                case Code.Ldflda:
                case Code.Ldc_I4:
                case Code.Ldc_I8:
                case Code.Ldc_R4:
                case Code.Ldc_R8:
                case Code.Ldstr:
                    stack.Push(inst);
                    break;
                case Code.Call:
                    PrintCallInstruction(stack, inst, methodContext);
                    break;
                case Code.Stloc:
                    PrintSetLocalInstruction(stack, inst, methodContext);
                    break;
                case Code.Stfld:
                    PrintSetFieldInstruction(stack, inst, methodContext);
                    break;
                case Code.Newobj:
                    PrintNewObjInstruction(stack, inst, methodContext);
                    break;
                case Code.Newarr:
                    PrintNewArrayInstruction(stack, inst, methodContext);
                    break;
                case Code.Stelem_Ref:

                    break;
            }

        }
    }
    Console.WriteLine();
}*/